package com.ruiyi.adrobot.web.socket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruiyi.adrobot.beans.*;
import com.ruiyi.adrobot.beans.socket.Heart;
import com.ruiyi.adrobot.beans.socket.SocketResult;
import com.ruiyi.adrobot.beans.socket.TaskExcuteResult;
import com.ruiyi.adrobot.service.accountinfo.AccountInfoService;
import com.ruiyi.adrobot.service.device.DeviceService;
import com.ruiyi.adrobot.service.robotinfo.RobotService;
import com.ruiyi.adrobot.service.taskdetail.TaskDetailService;
import com.ruiyi.adrobot.utils.DateUtil;
import com.ruiyi.adrobot.utils.DesUtils;
import com.ruiyi.adrobot.utils.LoggerUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description: Socket处理类
 * @author: tao.zeng
 * @date: 2017年5月3日 上午9:22:49
 */
@Component
public class RobotSocketHandler extends SimpleChannelInboundHandler<String> {
    private static RobotService robotInfoService;
    private static DeviceService deviceService;
    private static TaskDetailService taskDetailService;
    private static AccountInfoService accountInfoService;
    private static Set<String> excludeIp;

    static {
        excludeIp = new LinkedHashSet<>();
        excludeIp.add("183.61.51.59");
        excludeIp.add("183.232.175.5");
    }

    @Autowired
    public void setAccountInfoService(AccountInfoService accountInfoService) {
        RobotSocketHandler.accountInfoService = accountInfoService;
    }

    @Autowired
    public void setDeviceService(DeviceService deviceService) {
        RobotSocketHandler.deviceService = deviceService;
    }

    @Autowired
    public void setRobotInfoService(RobotService robotInfoService) {
        RobotSocketHandler.robotInfoService = robotInfoService;
    }

    @Autowired
    public void setTaskDetailService(TaskDetailService taskDetailService) {
        RobotSocketHandler.taskDetailService = taskDetailService;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        JSONObject jsonObj;
        try {
            jsonObj = JSON.parseObject(DesUtils.decrypt(msg));
        } catch (Exception e) {
            LoggerUtil.info("遭到攻击：" + ctx.channel().remoteAddress().toString().replace("/", "") + msg);
            robotOffLine(ctx);
            return;
        }
        String method = jsonObj.getString("method");
        JSONObject paramsJsonObject = JSONObject.parseObject(jsonObj.getString("params"));
        // 定义返回数据
        SocketResult socketResult = new SocketResult(true, method, null);
        switch (method) {
            case "robot_online":
                Map<String, Object> socketMap = new HashMap<>();
                // 保存robot基本信息
                // LoggerUtil.info("客户端上线");
                Robot robotInfo = paramsJsonObject.toJavaObject(Robot.class);
                robotInfo.setOnlineIp(ctx.channel().remoteAddress().toString().replace("/", ""));
                if (excludeIp.contains(robotInfo.getOnlineIp()) || robotInfo.getWifi().getSsid().equals("WiredSSID") || robotInfo.getBoard().equals("generic")) {
                    LoggerUtil.info("遭到攻击：" + robotInfo.getOnlineIp() + msg);
                    robotOffLine(ctx);
                    return;
                }
                Robot oldRobotInfo = robotInfoService.findBySimCode(robotInfo.getSimCode());
                if (StringUtils.hasText(robotInfo.getOsVersion()) && StringUtils.hasText(robotInfo.getApps())) {
                    if (null != oldRobotInfo) {
                        oldRobotInfo.setOnline(true);
                        oldRobotInfo.setVersion(robotInfo.getVersion());
                        oldRobotInfo.setOsVersion(robotInfo.getOsVersion());
                        oldRobotInfo.setApps(robotInfo.getApps());
                        oldRobotInfo.setOnlineIp(robotInfo.getOnlineIp());
                        // 判断手机品牌是否一致，以上线信息为准
                        if (null != oldRobotInfo.getModel() && !robotInfo.getModel().equals(oldRobotInfo.getModel())) {
                            // 以上线型号为准
                            oldRobotInfo.setBrand(robotInfo.getBrand());
                            oldRobotInfo.setModel(robotInfo.getModel());
                            oldRobotInfo.setDisplayId(robotInfo.getDisplayId());
                            oldRobotInfo.setVersionAndroid(robotInfo.getVersionAndroid());
                            oldRobotInfo.setBoard(robotInfo.getBoard());
                        }
                        if (null == oldRobotInfo.getWifi() || !robotInfo.getWifi().getSsid().equals(oldRobotInfo.getWifi().getSsid())) {
                            oldRobotInfo.setWifi(robotInfo.getWifi());
                            robotInfoService.saveOrUpdate(oldRobotInfo);
                        }
                        robotInfoService.saveOrUpdate(oldRobotInfo);
                    } else {
                        robotInfoService.add(robotInfo);
                    }
                    NettyChannelhandlerContextMap.add(robotInfo.getSimCode(), ctx);
                    LoggerUtil.info("当前连接数量：" + NettyChannelhandlerContextMap.getSize());
                    socketResult.setResult(socketMap);
                    ctx.writeAndFlush(socketResult.toString());
                } else {
                    LoggerUtil.info("遭到攻击:" + msg);
                    robotOffLine(ctx);
                }
                break;
            case "robot_excute_result":
                // Robot执行结果返回
                TaskExcuteResult taskExcuteResult = paramsJsonObject.toJavaObject(TaskExcuteResult.class);
                TaskDetail.Task nowTask = taskDetailService.findTaskByNum(taskExcuteResult.getTaskDetailId(), taskExcuteResult.getNum());
                if (!nowTask.getSuccess()) {
                    //更新任务状态
                    TaskDetail task = updateTaskStatus(taskExcuteResult);
                    // 保存老用户账号
                    saveAccountInfo(taskExcuteResult, task);
                    // 更新任务完成情况
                    updateTaskDetailStatus(taskExcuteResult);
                    ctx.writeAndFlush(socketResult.toString());
                } else {
                    LoggerUtil.info("重复接收数据:");
                }
                break;
            case "heart":
                // 修改Robot是否忙碌
                Heart heart = paramsJsonObject.toJavaObject(Heart.class);
                robotInfoService.updateRobotStatus(heart);
                NettyChannelhandlerContextMap.add(heart.getSimCode(), ctx);
                ctx.writeAndFlush("###");
        }
    }


    private void updateTaskDetailStatus(TaskExcuteResult taskExcuteResult) {
        Update taskDetailUpdate = new Update();
        taskDetailUpdate.inc("finishTime", 1);
        TaskDetail nowTaskDetail = taskDetailService.updateById(taskExcuteResult.getTaskDetailId(), taskDetailUpdate);
        if (nowTaskDetail.getTotalTime() <= nowTaskDetail.getFinishTime() + 1) {
            taskDetailService.updateById(taskExcuteResult.getTaskDetailId(), new Update().set("finished", true).set("completeTime", DateUtil.getCurrentTime()));
        }
    }

    private TaskDetail updateTaskStatus(TaskExcuteResult taskExcuteResult) {
        Update update = new Update();
        update.set("tasks.$.success", true);
        update.set("tasks.$.endTime", DateUtil.getCurrentTime());
        update.set("tasks.$.stepNum", taskExcuteResult.getStepNum());
        update.set("tasks.$.errorCode", taskExcuteResult.getErrorCode());
        return taskDetailService.updateTaskByNum(taskExcuteResult.getTaskDetailId(), taskExcuteResult.getNum(), update);
    }

    private void saveAccountInfo(TaskExcuteResult taskExcuteResult, TaskDetail task) {
        Device device = deviceService.findOne(taskExcuteResult.getImei());
        if (taskExcuteResult.getErrorCode() != 0) {
            deviceService.removeUsedFunsById(taskExcuteResult.getImei(), task.getFun(), device == null ? true : false);
            return;
        }
        if (!StringUtils.isEmpty(taskExcuteResult.getUserName())) {
            AccountInfo.Account account = new AccountInfo.Account(taskExcuteResult.getUserName(), taskExcuteResult.getPwd(), DateUtil.getCurrentTime(), taskExcuteResult.getImei(), device == null ? true : false, taskExcuteResult.getModel());
            accountInfoService.appendAccount(task.getFun(), account);
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
        robotOffLine(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        LoggerUtil.debug("新客户端连接");
        super.handlerAdded(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        robotOffLine(ctx);
        super.exceptionCaught(ctx, cause);
    }

    private void robotOffLine(ChannelHandlerContext ctx) {
        LoggerUtil.info("客户端断开");
        Robot oldRobotInfo = robotInfoService.findBySimCode(NettyChannelhandlerContextMap.remove(ctx));
        if (null != oldRobotInfo) {
            oldRobotInfo.setOnline(false); // 设置为离线状态
            oldRobotInfo.setOffLineTime(DateUtil.getCurrentTime());
            robotInfoService.saveOrUpdate(oldRobotInfo);
            LoggerUtil.info("当前连接数量：" + NettyChannelhandlerContextMap.getSize());
        }
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleState state = ((IdleStateEvent) evt).state();
            if (state == IdleState.ALL_IDLE) {
                // 25秒内未读写关闭客户端
                LoggerUtil.info("踢出客户端");
                robotOffLine(ctx);
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}
